/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
* http://connectionroad.com
*/

using System;
using System.Configuration;
using System.Deployment.Application;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
using CrSyncFilesForms.Properties;
using CrSyncFilesForms.Utils;
using CrSyncFilesUtils;

namespace CrSyncFilesForms
{
    static partial class Program
    {

        public static Main MainWindow { get; set; }
        public static Thread ThreadVerboseWindow { get; set; }
        public static CrSyncUiVerboseOutput VerboseWindow { get; set; }
        public static bool DebugEnabled { get; set; }
        public static bool ConsoleMode { get; set; }
        public static FileSystemWatcher UserConfigWatcher { get; set; }

        private static Mutex _defaultMutext;

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] applicationParameters)
        {

            string[] commandLineArguments;

            if (ApplicationDeployment.IsNetworkDeployed)
            {
                var clickOnceArgs = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;
                if (clickOnceArgs != null && clickOnceArgs.Length > 0)
                {
                    commandLineArguments = clickOnceArgs[0].Split(new[] { ' ' });
                }
                else
                {
                    commandLineArguments = new[]{""};
                }
            }
            else
            {
                commandLineArguments = applicationParameters;
            }


            

            try
            {
                ApplicationGlobals.InitializeApplicationConfiguration();
                bool instantiated;
                _defaultMutext = new Mutex(false, ApplicationGlobals.UniqueIdentifier, out instantiated);

                var regManager = new RegistryManager(@"Software\ConnectionRoad\CRSyncFiles");
                if (commandLineArguments != null && commandLineArguments.Length > 0)
                {
                    regManager.SetValueForKey(string.Join(" ", commandLineArguments), "DEBUG_PREVIOUS_ARGUMENTS");
                }
                
                /************************************************************************************************
                * Here we check for a flag that that lets current user defaults overwritten or replaced.
                * 
                * This will save us time from going through all the user directories and opening the userconfig
                * and editing it manually.
                * 
                * The reason that this is here on top rather than along with the rest of the flag checking codes
                * is that, we should allow writing to the current user defaults regardless of how many instance
                * of CRSyncFiles is running.
                ***********************************************************************************************/
                var cmdUserDefaults = new CrSyncFileCmdParser(commandLineArguments);
                if (cmdUserDefaults.ContainsParameters(new[] { "--user-defaults", "--write", "--set-user-defaults", "--settings", "--config", "--config", "--write-defaults" }, false))
                {
                    var cmdUserDefaultKey = new CrSyncFileCmdParser(commandLineArguments);
                    if (cmdUserDefaultKey.ContainsParameters(new[] { "--key", "--name", "--config-name" }, true))
                    {
                        var configKey = cmdUserDefaultKey.CurrentArgument;
                        var cmdUserDefaultType = new CrSyncFileCmdParser(commandLineArguments);
                        if (cmdUserDefaultType.ContainsParameters(new[] { "--type", "--data-type", "--config-type" }, true))
                        {
                            var configType = cmdUserDefaultType.CurrentArgument;
                            var cmdUserDefaultValue = new CrSyncFileCmdParser(commandLineArguments);
                            if (cmdUserDefaultValue.ContainsParameters(new[] { "--value", "--config-value" }, true))
                            {
                                var configValue = cmdUserDefaultValue.CurrentArgument;
                                ApplicationGlobals.WriteUserDefaults(configKey, configType, configValue);
                            }
                        }
                    }
                }


                // Check if cloud/azure flag is set. NOTE: Remove from codeplex
                var synchronizerManager = new FileSynchronizerManager(ConfigurationManager.AppSettings["SyncProvider"]);
                ApplicationGlobals.EnableCloud = synchronizerManager.IsCloudEnabled();

                if (instantiated)
                {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);

                    // Do silent import
                    ApplicationGlobals.SilentlyImportSynctoyFolderPairs();

                    // Check if user wants to bypass syncframework detection
                    var skipSyncFrameworkDetection = false;
                    var cmdSyncFramework = new CrSyncFileCmdParser(commandLineArguments);
                    if(cmdSyncFramework.ContainsParameters(new[]{"--skipframeworkdetection","--skip-framework-detection"},false))
                    {
                        skipSyncFrameworkDetection = true;
                    }


                    var syncFrameworkPresent = skipSyncFrameworkDetection || CrSyncFilesUtility.IsSyncFrameworkInstalled();
                    

                    if (syncFrameworkPresent)
                    {
                        CrSyncFilesMode runMode;
                        if ((commandLineArguments != null) && (commandLineArguments.Length >= 1))
                        {

                            /*******************************************************
                            * Here we parse for possible various flags and perform
                            * necessary steps before the main application window
                            * is loaded.
                            * 
                            * Add your flags here using CRSyncFileCmdParser class.
                            ******************************************************/
                            
                            // Check if reset window position flag is set
                            var cmdResetWindow = new CrSyncFileCmdParser(commandLineArguments);
                            if(cmdResetWindow.ContainsParameters(new[] {"--resetwindowpos","--resetwindowposition"}, false))
                            {
                                Settings.Default.CFG_WINDOW_HEIGHT = 600;
                                Settings.Default.CFG_WINDOW_WIDTH = 880;
                                Settings.Default.CFG_WINDOW_X = 50;
                                Settings.Default.CFG_WINDOW_Y = 50;
                                Settings.Default.Save();
                            }

                            // Check if verbose flag is set
                            var cmdVerboseParse = new CrSyncFileCmdParser(commandLineArguments);
                            ApplicationGlobals.Verbose = cmdVerboseParse.ContainsParameters(new[] { "-v", "--verbose" }, false);

                            

                            /****************************************************
                            * CRSyncFiles' main program supports running in window
                            * mode and console mode. Here we identify each type
                            * of run mode. 
                            * 
                            * When running in console, CRSyncFiles can parse
                            * two different types of command line argument
                            * structures: CRSyncFiles' CLI and SyncToy CLI.
                            * 
                            * CRSyncFiles' CLI is parsed using 
                            * CrSyncFilesConsoleUtility.RunConsoleUtility and
                            * can fully manage folder pairs through CLI:
                            *      - List available folder pairs
                            *      - Create folder pairs
                            *      - Rename folder pairs
                            *      - Delete folder pairs
                            *      - Update folder pairs
                            *      - Run folder pairs
                            * 
                            * 
                            * SyncToy CLI is parsed using CrSynctoyCmdParser and
                            * can only Create, Delete and Run folder pairs.
                            ***************************************************/
                            var cmdCliParse = new CrSyncFileCmdParser(commandLineArguments);
                            
                            // Check if the argument uses CRSyncFiles' command line structure
                            if (cmdCliParse.ContainsParameters(new[] { "--crsyncfiles", "--connectionroad", "~", "--cr", "--connectionroad", "-x", "--console" }, false))
                            {
                                // Use CRSyncFiles' command line structure
                                runMode = CrSyncFilesMode.CRSyncConsole;
                            }
                            else
                            {
                                // Use SyncToy's command line structure
                                var arguments = string.Join(" ", commandLineArguments);
                                if (arguments.StartsWith("-R") || arguments.StartsWith("-d") || arguments.StartsWith("-u") || arguments.StartsWith("-?"))
                                {
                                    runMode = CrSyncFilesMode.SyncToyConsole;
                                }
                                else
                                {
                                    runMode = CrSyncFilesMode.Windowed;
                                }
                            }


                        }
                        else
                        {
                            runMode = CrSyncFilesMode.Windowed;
                        }


                        if (runMode == CrSyncFilesMode.CRSyncConsole)
                        {
                            ConsoleMode = true;

                            // Handle arguments
                            var crsyncConsole = new CrSyncFilesConsoleUtility(ApplicationGlobals.MetaDataDirectory,
                                                                              ApplicationGlobals.Prefix,
                                                                              ApplicationGlobals.PairManager,
                                                                              ApplicationGlobals.SyncToyManager,
                                                                              ApplicationGlobals.CrSyncLogFilePath,
                                                                              ApplicationGlobals.
                                                                                  CrSyncCriticalLogFilePath);
                            crsyncConsole.Parse(commandLineArguments,Settings.Default, ConfigurationManager.AppSettings["SyncProvider"]);
                            
                        }
                        else if (runMode == CrSyncFilesMode.SyncToyConsole)
                        {
                            ConsoleMode = true;
                            var synctoyConsole = new CrSynctoyCmdParser(ApplicationGlobals.PairManager,
                                                                        ApplicationGlobals.SyncToyManager,
                                                                        ApplicationGlobals.MetaDataDirectory,
                                                                        ApplicationGlobals.Prefix,
                                                                        ApplicationGlobals.CrSyncLogFilePath,
                                                                        ApplicationGlobals.CrSyncCriticalLogFilePath);
                            synctoyConsole.Parse(commandLineArguments, Settings.Default, ConfigurationManager.AppSettings["SyncProvider"]);
                        }
                        else
                        {
                            if (CrSyncFilesUtility.IsSyncToyWindowPresent() && (string.IsNullOrEmpty(ConfigurationManager.AppSettings["AppDataPostFix"])) )
                            {
                                MessageBox.Show(
                                    "Microsoft SyncToy is currently Running."
                                    + "\nPlease Exit Microsoft SyncToy before Running CRSyncFiles.",
                                    "CrSyncFiles",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);
                            }
                            else
                            {

                                MainWindow = new Main();
                                if (ApplicationGlobals.Verbose)
                                {
                                    VerboseWindow = new CrSyncUiVerboseOutput();
                                    VerboseWindow.Show();
                                }
                                
                                // NOTE : Remore from codeplex
                                Application.ThreadExit += ApplicationAboutToExit;

                                Application.Run(MainWindow);
                            }
                        }
                    }
                    else
                    {

                        
                        CrSyncFilesUtility.WriteLog(Resources.MESSAGE_SYNC_FRAMEWORK_NOT_FOUND,
                            ApplicationGlobals.CrSyncCriticalLogFilePath);

                        MessageBox.Show(Resources.MESSAGE_SYNC_FRAMEWORK_NOT_FOUND,
                                        Resources.TEXT_SHORT_NAME,
                                        MessageBoxButtons.OK,
                                        MessageBoxIcon.Information);
                    }


                    
                }
                else
                {

                    // Write and error message to the critical errors log
                    const string multipleInstanceMessageText = "CRSyncFiles failed to launch.  This is most likely due to the fact that there was already a copy of CRSyncFiles running in Memory.  Note: This may have occurred during a scheduled operation.  Therefore, ensure that your Scheduled Folder Pairs are separated sufficiently in time.\r\n";

                    ApplicationGlobals.RegistryManager.SetValueForKey("True", "CFG_SHOW_CRITICAL_LOG");
                    CrSyncFilesUtility.WriteLog(multipleInstanceMessageText,ApplicationGlobals.CrSyncCriticalLogFilePath);
                    
                    // If this line is executed, it means there's already running process of the same application.
                    var processes = Process.GetProcessesByName("CrSyncFiles");
                    foreach (var process in processes)
                    {
                        CrSyncFilesUtility.SetForegroundWindow(process.MainWindowHandle); // Let's make the current instace focused.
                    }
                }
            }
            catch(Exception e)
            {

                ApplicationGlobals.RegistryManager.SetValueForKey("True", "CFG_SHOW_CRITICAL_LOG");
                var errorMessage = string.Format("[CONNECTION ROAD : DEBUG start ] --------------------\n\r\n\r "
                                                   +"ERROR MESSAGE:\n\r{0}"
                                                   +"\n\rCULPRIT: \n\r{1}"
                                                   +"\n\rSTACK TRACE: \n\r{2}"
                                                   +"\n\r[CONNECTION ROAD : DEBUG END ] --------------------\n\r\n\r" 
                                                   ,e.Message
                                                   , e.Source
                                                   , e.StackTrace);
                
                // Write to event viewer
                ApplicationGlobals.WriteEventLog(errorMessage,EventLogEntryType.Error);
                
                // Write to CRSyncFiles' very own log file
                //CrSyncFilesUtility.WriteLog(errorMessage, ApplicationGlobals.CrSyncLogFilePath);
                CrSyncFilesUtility.WriteLog(errorMessage, ApplicationGlobals.CrSyncCriticalLogFilePath);

                // Also write to verbose log
                ApplicationGlobals.SetVerboseMessage(errorMessage) ;

               // Also write to standard I/O when possible););
                if(ConsoleMode)
                {
                    // Safely uncomment below this line
                    Console.WriteLine(errorMessage);
                    Console.WriteLine("\n\n\nHit Enter Key to exit.");
                    Console.ReadLine();
                }
                else
                {
                    // This is a dialog that displays error messages and error stacks.
                    // You can enable this temporarily for debugging.
                    // var errorDialog = new CrSyncFilesErrorDialog(e.Message,e);
                    // errorDialog.ShowDialog();

                    var errorDialog = new CrSyncUiExceptionDialog();
                    errorDialog.ShowDialog();
                }
            }
        }

        private static void UiTreadVerboseWindow()
        {
            Application.Run(new CrSyncUiVerboseOutput());
        }

        public static void SpawnDiagnosticWindow()
        {
            ThreadVerboseWindow = new Thread(UiTreadVerboseWindow);
            ThreadVerboseWindow.IsBackground = false;
            ThreadVerboseWindow.SetApartmentState(ApartmentState.MTA);
            ThreadVerboseWindow.Start();
        }


        public static void ApplicationAboutToExit(object  sender, EventArgs args)
        {
            OnExitExtension();
        }

        static partial void OnExitExtension();


    }
}
